plugin stats implementation#220
Conversation
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
Note
|
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Title check | ✅ Passed | The pull request title 'plugin stats implementation' accurately describes the main change—adding a comprehensive telemetry system to track and report plugin installation and download statistics. |
| Linked Issues check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Out of Scope Changes check | ✅ Passed | Check skipped because no linked issues were found for this pull request. |
| Description check | ✅ Passed | The pull request description accurately describes the changeset, explaining the new plugin stats tracking feature, database table, endpoints, and IP hashing implementation. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing Touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
ft/plugin_Stats
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@PluginBuilder/Controllers/ApiController.cs`:
- Around line 224-226: The GetPluginStats anonymous endpoint in ApiController
(method GetPluginStats) lacks the public API rate limiting attribute; add the
[EnableRateLimiting(Policies.PublicApiRateLimit)] attribute to the
GetPluginStats action (above the method, alongside [AllowAnonymous] and
[HttpGet("plugins/{pluginSlug}/stats")]) so it uses the same PublicApiRateLimit
policy as other public read endpoints.
- Around line 217-218: The code is mapping snapshot Identifier into telemetry
Slug, which fragments per-plugin stats; change the projection that builds
pluginReports so PluginReport is constructed with the plugin's canonical slug
(e.g., p.Slug or the field used by download telemetry) instead of p.Identifier —
e.g., replace new PluginReport(p.Identifier, p.Version) with new
PluginReport(p.Slug ?? p.Identifier, p.Version) or, preferably, new
PluginReport(p.Slug, p.Version) to ensure telemetry Slug comes from the
download/telemetry slug field rather than the snapshot Identifier.
- Around line 217-221: The current code in ApiController builds pluginReports
and always calls telemetryService.RecordServerSnapshot even when pluginReports
is empty; change this by adding a guard: after building pluginReports (the
variable), check if pluginReports.Any() (or Count > 0) and only call
telemetryService.RecordServerSnapshot(remoteIp, userAgent, pluginReports,
xOriginalFor, xForwardedFor) when non-empty, otherwise skip the call and still
return Ok(); this prevents sending empty telemetry that marks installs as
uninstalled.
In `@PluginBuilder/Services/TelemetryService.cs`:
- Around line 139-161: The uninstall SQL currently decrements install_count
(plugin_server_installs.install_count) which makes GetStats (method GetStats
returning PluginStats) undercount total installs; instead stop mutating
install_count on uninstall and introduce/maintain separate cumulative counters
(e.g., cumulative_installs and cumulative_uninstalls) or an events table for
installs/uninstalls; update the uninstall UPDATE that sets uninstalled_at to no
longer decrement install_count and to increment the cumulative_uninstalls
counter (or insert an uninstall event), and change the GetStats query to compute
TotalInstalls and TotalUninstalls from those cumulative columns/events while
deriving ActiveInstalls from COUNT(*) FILTER (WHERE uninstalled_at IS NULL)
(references: plugin_server_installs, install_count, uninstalled_at, GetStats,
PluginStats).
- Around line 88-131: The payload may contain duplicate plugin.Slug values
causing the upsert loop to attempt an INSERT twice and violate the (hashed_ip,
plugin_slug) PK; before computing reportedSlugs and iterating, deduplicate
pluginList by Slug using a case-insensitive grouping (so reportedSlugs and the
foreach use the deduped collection), then proceed with the existing
existingBySlug/foreach logic referencing pluginList and plugin.Slug; this
ensures each slug is processed exactly once and avoids duplicate INSERT
attempts.
- Around line 218-223: The current logic in TelemetryService that trims
forwarded IPs (the raw variable before IPAddress.TryParse) incorrectly splits
plain IPv6 addresses; update the parsing so bracketed addresses are handled by
extracting text between '[' and ']' when raw.Contains("]:")/raw.Contains(']'),
and only strip a trailing port by splitting on ':' when the string is an IPv4
with a port (detect by presence of '.' or by there being exactly one ':'),
otherwise leave raw intact for IPv6 (multiple ':'s). Modify the block that
currently uses raw.Contains(':') && !raw.Contains('.') to instead detect
IPv4-with-port (e.g., raw.Contains('.') || raw.Count(c=>c==':')==1) before
splitting so IPv6 addresses reach IPAddress.TryParse unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b7fc9523-5951-4cc4-af82-3ff61269f733
📒 Files selected for processing (7)
PluginBuilder/APIModels/InstalledPluginRequest.csPluginBuilder/Controllers/ApiController.csPluginBuilder/Data/Scripts/22.IncludePluginStatesTable.sqlPluginBuilder/DataModels/PluginDownload.csPluginBuilder/Program.csPluginBuilder/Services/TelemetryService.csPluginBuilder/ViewModels/Plugin/PluginTelemetryRequest.cs
| var pluginReports = plugins.Where(p => !string.IsNullOrWhiteSpace(p.Identifier) && !string.IsNullOrWhiteSpace(p.Version)) | ||
| .Select(p => new PluginReport(p.Identifier, p.Version)).ToList(); |
There was a problem hiding this comment.
Don’t map Identifier directly to telemetry Slug.
This stores mixed keys (identifier from snapshot vs slug from download telemetry), fragmenting per-plugin stats and making /plugins/{pluginSlug}/stats inconsistent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@PluginBuilder/Controllers/ApiController.cs` around lines 217 - 218, The code
is mapping snapshot Identifier into telemetry Slug, which fragments per-plugin
stats; change the projection that builds pluginReports so PluginReport is
constructed with the plugin's canonical slug (e.g., p.Slug or the field used by
download telemetry) instead of p.Identifier — e.g., replace new
PluginReport(p.Identifier, p.Version) with new PluginReport(p.Slug ??
p.Identifier, p.Version) or, preferably, new PluginReport(p.Slug, p.Version) to
ensure telemetry Slug comes from the download/telemetry slug field rather than
the snapshot Identifier.
This PR introduces plugin stats on the plugin builder..
Tracks plugin installs, updates, and uninstalls across BTCPay servers plugins.
New table plugin_server_installs stores one row per server/plugin pair, updated on every download and periodic snapshot report from BTCPay.
New endpoint POST /api/v1/telemetry/plugins receives the full installed plugin list from BTCPay periodically and diffs it against stored state to detect changes.
New endpoint GET /api/v1/plugins/{slug}/stats returns total installs, active installs, uninstalls, and version breakdowns for a plugin.
Server IPs are hashed before storage.